/* routines to use global raw socket - P2 */

/* for C++ compilers, use extern "C" */
#ifdef __cplusplus
extern "C" {
#endif

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#include <sys/errno.h>
//#include <linux/ip.h>
//#include <linux/tcp.h>

//includes for libpcap, sting, capture.c - S
#include <sys/param.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include "sting/gmt2local.h"
#include "pcap.h"
  //#include <linux/net_device.h>
#include "sting/inet.h"
#include "sting/capture.h" 
#include "nd.h"

#define MAX_INTERFACE 15
#define DEFAULT_SNAPLEN 1500
#define EMULAB

#define ISECHO(p, s)     (((p)->ip.ip_src == (s)) && \
     ((p)->ip.ip_p == IPPROTOCOL_TCP))

pcap_t **pc;             /* pcap device */
int *datalinkOffset;     /* offset of ip packet from datalink packet */
char* filter_command;

extern char ultcp_cnt;
extern struct net_device** ultcp_interface;
extern char ultcp_used[20];

int captureDebug = 1;
  
unsigned int thisTimeZone;
  
  //extern char * sourceIp; 

  //  char * iptab1, *iptab2, *iptab3, *iptab4, *iptab5; 
int siz; 
int verbose =1;

int global_socket = -1;
int send_socket[20];
int start_your_engines = 0;
struct sockaddr_in glob_sock;

fd_set readset;
struct timeval timeout;

int get_interface_loc(int ip);

uint32 sourceIpAddress; //the ip address of the local source
char name[MAXHOSTNAMELEN]; //the name of the local m/c

//-----------------
/* Capturing functions...
 * Derived from Savage's sting.c
 * All stuff from here till the next --- sentence
 * is NEW
 */

char* GetCaptureFilter(){
  return filter_command/*[i]*/;
}

void CaptureFilter(char *str){
  char filtercmds[1024];
  struct bpf_program filter;
  bpf_u_int32 mask,net,i;

  //copy the filter!
  if (filter_command/*[id]*/!=NULL)
    free(filter_command/*[id]*/);
  filter_command/*[id]*/ = (char*)malloc(strlen(str)+2);
  memcpy(filter_command/*[id]*/,str,strlen(str)+1);

  sprintf(filtercmds,"tcp and %s", str);

  if (captureDebug) {
      //printf("filter = %s\n", filtercmds);
  }

  for (i=0;i<ultcp_cnt;i++){
    if (!ultcp_used[i])
      continue;

    if (pcap_lookupnet(ultcp_interface[i]->name, &net, &mask, filtercmds) == -1) {
      fprintf(stderr, "Can't get netmask for device %s\n", ultcp_interface[i]->name);
      net = 0;
      mask = 0;
    }

    //sprintf(filtercmds,"tcp and dst host %s",sprint_ip(ultcp_interface[i]->ip_addr));

    printf("Set capture on %s filter %s\n",ultcp_interface[i]->name,filtercmds);


    if (pcap_compile(pc[i], &filter, filtercmds, 1, mask) < 0) {
      fprintf(stderr, "Error when running filter %s: %s", filtercmds,pcap_geterr(pc[i]));
      exit(-1);
    }
    
    if (pcap_setfilter(pc[i], &filter) < 0) {
      fprintf(stderr, "Can't set filter: %s",pcap_geterr(pc[i]));
      exit(-1);
    }
  }
  //printf("\n");
}

void apply_filter(){
  char* filter;
  if (filter_command!=NULL){
    filter = (char*)malloc(strlen(filter_command)+2);
    memcpy(filter,filter_command,strlen(filter_command)+1);
    CaptureFilter(filter);
  }
}

int remote_ports[10];
int remote_cnt = 0;;

void filter_remote_port(int port, int id){
  char cmd[1024];
  char *srcIP,*e;
  char remotePort[10];
  int siz;int i, found;

  found = 0;
  for (i=0;i<remote_cnt;i++)
    if (remote_ports[i]==port){
      found = 1;
      break;
    };

  if (found)
    return;

  srcIP = sprint_ip(ultcp_interface[id]->ip_addr);

  sprintf(remotePort,"%d",port);
  remote_ports[remote_cnt++] = port;

  e = GetCaptureFilter(id);
  if (e!=NULL)
    sprintf(cmd,"%s or src port %s"/* and dst host %s"*/,e,remotePort/*,srcIP*/);
  else
    sprintf(cmd,"src port %s"/* and dst host %s"*/,remotePort/*,srcIP*/);

  CaptureFilter(cmd);

    //now add iptables rules
  sprintf(cmd, "iptables -A INPUT -p tcp --source-port %s -j DROP", remotePort/*,srcIP*/);//--destination %s 
  //  e[siz] = '\0';
  printf("Running %s\n",cmd);
  
  system(cmd);
}

void filter_remote(int port, int ip){
  int all,i,id;
  all = 0;
  id = -1;
  if (ip){
    for (i=0;i<ultcp_cnt;i++){
      if (ultcp_interface[i]->ip_addr==ip)
	{
	  id = i;
	  break;
	}
    }
    if (id==-1){
      printf("IP is %s but can't find it\n",sprint_ip(ip));
      exit(1);
    }
  }
  else all = 1;
  
  if (!all)
    filter_remote_port(port,id);
  else
    for (i=0;i<ultcp_cnt;i++)
      filter_remote_port(port,i);
}

int local_ports[10];
int local_cnt = 0;

void filter_local_port(int port,int id){
  //now add iptables rules
  char cmd[1024];
  char localPort[10];
  int siz;
  char *e, *srcIP;
  int i, found;

  found = 0;
  for (i=0;i<local_cnt;i++)
    if (local_ports[i]==port){
      found = 1;
      break;
    };

  if (found)
    return;

  local_ports[local_cnt++] = port;
  srcIP = sprint_ip(ultcp_interface[id]->ip_addr);

  sprintf(localPort,"%d",port);

  e = GetCaptureFilter(id);
  if (e!=NULL)
    sprintf(cmd,"%s or dst port %s"/* and dst host %s"*/,e,localPort/*,srcIP*/);
  else
    sprintf(cmd,"dst port %s"/* and dst host %s"*/,localPort/*,srcIP*/);

  CaptureFilter(cmd);

  sprintf(cmd, "iptables -A INPUT -p tcp --destination-port %s -j DROP", localPort/*,srcIP*/); //--destination %s
  printf("Running %s\n",cmd);
  system(cmd);
}


void filter_local(int port, int ip){
  int all,i,id;
  all = 0;
  id = -1;
  if (ip){
    for (i=0;i<ultcp_cnt;i++){
      if (ultcp_interface[i]->ip_addr==ip)
	{
	  id = i;
	  break;
	}
    }
    if (id==-1){
      printf("IP is %s but can't find it\n",sprint_ip(ip));
      exit(1);
    }
  }
  else all = 1;
  
  if (!all)
    filter_local_port(port,id);
  else
    for (i=0;i<ultcp_cnt;i++)
      filter_local_port(port,i);
}

void init_interface(int i){
    int snaplen = DEFAULT_SNAPLEN;
    char errbuf[PCAP_ERRBUF_SIZE];
    int promisc = 0;
    int timeout = -1;  /* timeout in 1 second (10 ms) */
    int tmp;

    struct bpf_program filter;
    bpf_u_int32 mask = 0,net = 0;

    pc[i] = pcap_open_live(ultcp_interface[i]->name, snaplen, promisc, timeout, errbuf);

    if (pc[i] == NULL) {
      fprintf(stderr,"Can't open capture device %s: %s, %d\n",ultcp_interface[i]->name, errbuf,i);
      exit(-1);
    }

    if (i==0){
      printf("pcap version %d %d\n",pcap_major_version(pc[i]),pcap_minor_version(pc[i]));
    }
	

    if ((tmp = pcap_setdirection(pc[i],PCAP_D_IN))<0){
      fprintf(stderr,"Unable to set direction for %s\n",ultcp_interface[i]->name);
      exit(-1);
    };

    if ((tmp = pcap_setnonblock(pc[i],1,errbuf))<0){
      fprintf(stderr,"Unable to set nonblock for %s\n",ultcp_interface[i]->name,errbuf);
      exit(-1);
    };

      
      //else printf("pcap open live succeeded");

    /* XXX why do we need to do this? */
    tmp = pcap_snapshot(pc[i]);
    if (snaplen < tmp) {
      fprintf(stderr, "Warning: snaplen raised to %d from %d",snaplen, tmp);
    }

    if ((tmp = pcap_datalink(pc[i])) < 0) {
      fprintf(stderr,"Unable to determine datalink type for %s: %s\n",ultcp_interface[i]->name, errbuf);
      exit(-1);
    }
    switch(tmp) {
    case DLT_EN10MB: datalinkOffset[i] = 14; break;
    case DLT_IEEE802: datalinkOffset[i] = 22; break;
    case DLT_NULL: datalinkOffset[i] = 4; break;
    case DLT_SLIP:
    case DLT_PPP: datalinkOffset[i] = 24; break;
    case DLT_RAW: datalinkOffset[i] = 0; break;
    default:
      fprintf(stderr,"Unknown datalink type %d\n",i);
      exit(-1);
      break;
    }
    
    if (captureDebug) {
      //printf("Listening on %s, data link offset %d...\n", ultcp_interface[i]->name,datalinkOffset[i]);
    }
    
    ultcp_used[i] = 1;

    apply_filter();
}

void CaptureInit()
{
    //if (filter == "prot tcp and ... ") then str == ...; - S//

    char source[18];
    char target[18];
    int i;

    /* Get local time zone for interpreting timestamps */
    /* XXX - does this belong here? */
    thisTimeZone = gmt2local(0);

    //    device = pcap_lookupdev(errbuf);
    //if (device == NULL) {
    //fprintf(stderr, "Can't find capture device: %s\n", errbuf);
    //exit(-1);
    //}
    //if (captureDebug) {
    //	printf("Capture device name is %s\n", device);
    //}
    filter_command = NULL;//(char**)malloc(MAX_INTERFACE*sizeof(char*));
    pc = (pcap_t**)malloc(MAX_INTERFACE*sizeof(pcap_t*));
    datalinkOffset = (int*)malloc(MAX_INTERFACE*sizeof(int));
    
    //for (i=0;i<MAX_INTERFACE;i++)
    //filter_command[i] = NULL;
}

char *CaptureGetPacket(int id)
{
    const u_char *p;
    struct PacketInfo pi;

    p = pcap_next(pc[id], (struct pcap_pkthdr *)&pi);
    if (p!= NULL) {
      //      printf("\npcap_next is NOT null"); 
      p += datalinkOffset[id];
      //printf("id is %d ultcp %s",id, ultcp_interface[id]->name);
    }
    else {
      //      printf("\npcap_next returned NULL"); fflush(stdout); 
    }
    pi.ts.tv_sec = (pi.ts.tv_sec + thisTimeZone) % 86400;     
    return (char *)p;
}


void CaptureEnd()
{
    struct pcap_stat stat;
    int i;

    for (i=0;i<ultcp_cnt;i++){
      if (!ultcp_used[i])
	continue;

      if (pcap_stats(pc[i], &stat) < 0) {
	(void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pc[i]));
      }
      else {
	(void)fprintf(stderr, "Interface %s %s: rcvd %d dropped %d\n", ultcp_interface[i]->name,sprint_ip(ultcp_interface[i]->ip_addr), stat.ps_recv, stat.ps_drop);
      }
      
      pcap_close(pc[i]);
    }
}

//Extracting the host's name and IP address
int
GetCannonicalInfo(char *string, char name[MAXHOSTNAMELEN], uint32 *address)
{
    struct hostent *hp;

    /* Is string in dotted decimal format? */
    if ((*address = inet_addr(string)) == INADDR_NONE) {
	/* No, then lookup IP address */
	if ((hp = gethostbyname(string)) == NULL) {
	    /* Can't find IP address */
	    printf("ERROR: couldn't obtain address for %s\n", string);
	    return -1;
	} else {
//	    printf("\t\t\nstring:%p, address:%p, length:%d, name:%p\n", string, address, hp->h_length, name);
	    strncpy(name, hp->h_name, MAXHOSTNAMELEN-1);
	    name[MAXHOSTNAMELEN-1]=0;
	    memcpy((void *)address, (void *)hp->h_addr, hp->h_length);
	}
    } else {
	if ((hp = gethostbyaddr((char *)address, sizeof(*address),
				AF_INET)) == NULL) {
	    /* Can't get cannonical hostname, so just use input string */
	    if (verbose) {
		printf("WARNING: couldn't obtain cannonical name for %s\n", string);
	    }
	    strncpy(name, string, MAXHOSTNAMELEN - 1);
	} else {
	    strncpy(name, hp->h_name, MAXHOSTNAMELEN - 1);
	}
    }
    return 0;
}

char *InetAddress(uint32 addr)
{
    struct in_addr s;
    s.s_addr = addr;
    return (inet_ntoa(s));
}

void Quit(){
    fflush(stdout);
    fflush(stderr);
    exit(1);
}

//-----------------------------

void clean_pcap_ipchains(int a){
    printf("\n\n\n-->Flushing chains and pcap....\n");

    system("iptables -F");
    CaptureEnd();
    fprintf(stderr, "Done. Exiting\n");
    exit(0); //exiting
}

void create_global_socket(void)
{
  int on = 1,i;
    char source[MAXHOSTNAMELEN];
    char sourceStr[18];
    char cmd[100];
    char* e;

    if ((global_socket = socket(AF_INET,SOCK_RAW,IPPROTO_RAW)) < 0)  {
	perror("global socket");
	exit(1);
    }
    if (setsockopt(global_socket,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0)  {
	perror("global socket hdrincl");
	exit(1);
    }

    memset(&glob_sock,'\0',sizeof(glob_sock));
    glob_sock.sin_family = AF_INET;

// setup polling
    FD_ZERO(&readset);
    FD_SET(global_socket,&readset);

    if(gethostname(source, MAXHOSTNAMELEN) != 0){
	printf("Error: Cannot determine local host name.");
	Quit();
    }
    printf("\t\t\tsource before: [%s]\n\n\n", source);
    //strcpy(source, "9.2.11.159");

    //strcpy(source, sourceIp); 

    //if(GetCannonicalInfo(source, name, &sourceIpAddress) < 0)
    //Quit();

    //    if (InetAddress(sourceIpAddress)<0){
    //fprintf(stderr, "Invalid source address.\n");
    //exit(0);
    //}

    //strncpy(sourceStr, InetAddress(sourceIpAddress), sizeof(sourceStr)-1);

    //store the local address
    //printf("source = %s [%s]\n", name, InetAddress(sourceIpAddress));

    CaptureInit();

    //flush iptables
    //printf("flush\n");
    system("iptables -F");
   
    //store the local address
    if(gethostname(source, MAXHOSTNAMELEN) != 0){
	printf("Error: Cannot determine local host name.");
	Quit();
    }
}

  void send_on_global_socket(char *skb,unsigned long dest, unsigned long src,unsigned short len)
{
    struct in_addr daddr;
    int retval,i;

    //printf("\t\t\t\tDisplay inside send_on_glon, skb is %x \n", skb);
    //display_packet(skb);
    //printf("dest: %x, ntohl: %x\n", dest, ntohl(dest));
    daddr.s_addr = dest; //((struct iphdr *)skb)->daddr;
//len = ntohs(((struct iphdr *)skb)->tot_len);

    //printf("send : destination is %x, len is: %d\n",daddr.s_addr, len);
    //printf("test: send address is [%ux=%s]\n", daddr, inet_ntoa(daddr));
    glob_sock.sin_addr = daddr;

    //    printf("Packet: skb:[%s], its length:[%d], len: [%d]... Socket: %d\n", skb, strlen(skb), len, global_socket);

    //find device to send from

    i = get_interface_loc(src);
    //printf("Interface source is %d name %s\n",i,ultcp_interface[i]->name);
    if (i<0){
      printf("Cannot find interface for source address %s!\n",sprint_ip(src));
      exit(1);
    }

    if (send_socket[i]==0){
      int on = 1;
      struct sockaddr_in addr;

      if ((send_socket[i] = socket(AF_INET,SOCK_RAW,IPPROTO_RAW)) < 0)  {
	perror("global sendsocket");
	exit(1);
      }
      if (setsockopt(send_socket[i],IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0)  {
	perror("global socket hdrincl");
	exit(1);
      }

      addr.sin_family = AF_INET;
      addr.sin_addr.s_addr = src;
      addr.sin_port = 1300;
      if (bind(send_socket[i],(struct sockaddr*)&addr,sizeof(struct sockaddr_in))<0){
	perror("global socket bind to");
	exit(1);
      }
      /*      if (setsockopt(send_socket[i],SOL_SOCKET,SO_BINDTODEVICE,(char *)&ultcp_interface[i]->name,strlen(ultcp_interface[i]->name)) < 0)  {
	perror("global socket bind to device");
	exit(1);
	}*/
    }

    retval = sendto(send_socket[i],skb,len,0x0,(struct sockaddr *)&glob_sock,sizeof(glob_sock));
    if (retval != len)  {
	perror("sendto");
	exit(1);
    }

    //    printf("retval from send was %d \n",retval);
    //printf("\t\t\tVoila successsssssssssss\n\n\n\n");

}

  /*int recv_on_global_socket(char *buf)
{
//rendered  useless  -S
    struct sockaddr saddr;
    int mesglen;
    int fromlen = sizeof(saddr);
    struct PacketInfo pi; //information abt the packet
    struct TcpPacket *p;
 
    if (!FD_ISSET(global_socket,&readset)) {
	printf("select shows activity but FD isn't set!\n");
	fflush(stdout);
	return 0;
    }
    //-S
    //look for captured packets... drop echoes
    // this call should not block, since we do this when select tells us to -P2
    p = (struct TcpPacket *) CaptureGetPacket(&pi);
    if( ISECHO(p, sourceIpAddress) )
	return 0;
    else
	return ((p->ip).ip_len);
	}*/

#include <stdio.h>

  int cc = 0;
int poll_global_socket(char **buf)
{

//Assuming that tbe buffer is large enough, why? -S
//return the length of the packet received, if at all

    int ret;
    static int crt = -1;
    struct sockaddr saddr;
    int mesglen, i,fnd;
    int fromlen = sizeof(saddr);
    struct TcpPacket *p;

    // Non-blocking read off the pcap filter -S
    // look for captured packets... drop echoes

    fnd = 0;
    for (i = 0;i<ultcp_cnt;i++){
      crt = (crt+1)%ultcp_cnt;
      if (ultcp_used[crt]){
	p = (struct TcpPacket *) CaptureGetPacket(crt);
	if(p != NULL) {
	  fnd = 1;
	  break;
	}
      }
    }

    if (!fnd) return 0;

    //    if (i!=0){
    //printf("Recv on other iface!!!\n");
    //}
    //    printf("\ncaptureGetPacket returned something useful... "); 
    //printf("Recv packet on %s\n",ultcp_interface[i]->name);

    *buf = (char *)p;
    mesglen = ntohs((p->ip).ip_len);
    if (ISECHO(p, ultcp_interface[i]->ip_addr)){
      printf("\n Echo packet found..."); 
      return 0;
    }
    else{
      //fprintf(stderr,"got a packet len = %d\n",mesglen);
      
      //	printf("got a packet len = %d\n",mesglen);
      *buf = (char *)p; //throw out the pointer
      return (ntohs((p->ip).ip_len));
    }
}

#ifdef __cplusplus
}
#endif
